/**
 * 
 */
package juegos;

import java.util.Vector;


/**
 * @author DaNieLooP
 *
 *Definimos los nodos del 8-puzzle
 */
public class Nodo8Puzzle extends Nodo{
	
	private int tablero[][]=new int [3][3];
	private int x;
	private int y;
	
	/**
	 * Constructora de clase.
	 * 
	 * Define un estado inicial y la heuristica que se va a emplear
	 * que en este caso es la distancia de manhattan
	 */
	public Nodo8Puzzle(){
		this.informado=true;
		this.coste=0;
		this.profundidad = 0;
		this.camino="Estado inicial,";
		tablero[0][0]=2;tablero[0][1]=1;tablero[0][2]=6;
		tablero[1][0]=4;tablero[1][1]=0;tablero[1][2]=8;
		tablero[2][0]=7;tablero[2][1]=5;tablero[2][2]=3;
		x=1;y=1;
		this.vHeur=calculaManhattan();
		this.calculaEstado();
	}
	
	/**
	 * Constructora de clase con parametros
	 * 
	 * @param tab tablero de juego
	 * @param a posicion x
	 * @param b posicion y
	 * @param op operador que vamos a usar para mover
	 * @param cam camino que llevamos recorrido
	 * @param valor 
	 * @param prof profundidad del nodo con el que llamamos
	 */
	public Nodo8Puzzle(int tab[][],int a,int b,int op,String cam,int valor,int prof){
		this.informado=true;
		for (int i=0;i<3;i++)
			for (int j=0;j<3;j++)
				tablero[i][j]=tab[i][j];
		this.x=a;
		this.y=b;
		this.coste=valor+1;
		this.profundidad = prof+1;
		String mov;
		switch (op){
		case 1: mov = (" Arriba,"); break;
		case 2: mov = (" Derecha,"); break;
		case 3: mov = (" Abajo,"); break;
		case 4: mov = (" Izquierda,"); break;	
		default: mov = ("Este movimiento no existe.");
		}		
		this.camino = (cam + mov);
		this.calculaEstado();
	}
	/**
	 * Calculamos el valor de la distancia de manhattan total del tablero
	 * 
	 * @return devuelve la suma total de distancias horinzontales y verticales
	 */
	private int calculaManhattan(){
		int suma=0;
		for (int i=0;i<3;i++){
			for (int j=0;j<3;j++){
				switch (tablero[i][j]){
				case 0: suma=suma+0; break;
			    case 1:suma=suma+i+j; break;
				case 2: if (j!=1) suma=suma+1;suma=suma+i; break;
				case 3: suma=suma+(2-j)+i; break;
				case 4: if (i!=1) suma=suma+1; suma=suma+2-j; break;
				case 5: suma=suma+(2-i)+(2-j); break;
				case 6: if (j!=1) suma=suma+1;suma=suma+2-i; break;
				case 7: suma=suma+j+(2-i); break;
				case 8: if (i!=1) suma=suma+1; suma=suma+j; break;
				default: suma = -1;
				}
			}
		}
		for (int i=0;i<3;i++)
			for(int j=0;j<3;j++)
					switch (tablero[i][j]){
					case 0: suma=suma+0; break;
				    case 1: if (i==0&j==1&tablero[0][0]==2)suma=suma+2;break;
					case 2: if (i==0&j==2&tablero[0][1]==3) suma=suma+2;break;
					case 3: if (i==1&j==2&tablero[0][2]==4) suma=suma+2;break;
					case 4: if (i==2&j==2&tablero[1][2]==5) suma=suma+2;break;
					case 5: if (i==2&j==1&tablero[2][2]==6) suma=suma+2;break;
					case 6: if (i==2&j==0&tablero[2][1]==7) suma=suma+2;break;
					case 7: if (i==1&j==0&tablero[2][0]==8) suma=suma+2;break;
					case 8: if (i==0&j==0&tablero[1][0]==1) suma=suma+2;break;
					default: suma = -1;
					}			
		return suma;
	}
	/**
	 * Mueve el hueco hacia arriba
	 * 
	 * @return el nodo que esta situado encima del actual
	 */
	private Nodo8Puzzle Arriba(){
		Nodo8Puzzle nuevo=null;
		if (x!=0){
			nuevo=new Nodo8Puzzle(tablero,x,y,1,this.camino,this.coste,this.profundidad);
			int aux=nuevo.tablero[x-1][y];
			nuevo.tablero[x-1][y]=nuevo.tablero[x][y];
			nuevo.tablero[x][y]=aux;
			nuevo.x=x-1;			
			nuevo.vHeur=nuevo.calculaManhattan();
			nuevo.calculaEstado();
		}
		return nuevo;
	}
	
	/**
	 * Mueve el hueco hacia la derecha
	 * 
	 * @return el nodo situado a la derecha del actual
	 */
	private Nodo8Puzzle Derecha(){
		Nodo8Puzzle nuevo=null;
		if (y!=2){
			nuevo=new Nodo8Puzzle(tablero,x,y,2,this.camino,this.coste,this.profundidad);
			int aux=nuevo.tablero[x][y+1];
			nuevo.tablero[x][y+1]=nuevo.tablero[x][y];
			nuevo.tablero[x][y]=aux;
			nuevo.y=y+1;
			nuevo.vHeur=nuevo.calculaManhattan();
			nuevo.calculaEstado();
		}
		return nuevo;
	}
	
	/**
	 * Mueve el hueco hacia abajo
	 * 
	 * @return el nodo situado debajo del actual
	 */
	private Nodo8Puzzle Abajo(){
		Nodo8Puzzle nuevo=null;
		if (x!=2){
			nuevo=new Nodo8Puzzle(tablero,x,y,3,this.camino,this.coste,this.profundidad);
			int aux=nuevo.tablero[x+1][y];
			nuevo.tablero[x+1][y]=nuevo.tablero[x][y];
			nuevo.tablero[x][y]=aux;
			nuevo.x=x+1;
			nuevo.vHeur=nuevo.calculaManhattan();
			nuevo.calculaEstado();
		}
		return nuevo;
	} 
	
	/**
	 * Mueve el hueco a la izquierda
	 * 
	 * @return el nodo situado a la izquierda del actual
	 */
	private Nodo8Puzzle Izquierda(){
		Nodo8Puzzle nuevo=null;
		if (y!=0){
			nuevo=new Nodo8Puzzle(tablero,x,y,4,this.camino,this.coste,this.profundidad);
			int aux=nuevo.tablero[x][y-1];
			nuevo.tablero[x][y-1]=nuevo.tablero[x][y];
			nuevo.tablero[x][y]=aux;
			nuevo.y=y-1;			
			nuevo.vHeur=nuevo.calculaManhattan();
			nuevo.calculaEstado();
		}
		return nuevo;
	} 
	/**
	 * Calcula la el valor de la heuristica en un momento dado
	 */
	public void calculaHeur(){
		this.vHeur =calculaManhattan();
	}
	/**
	 * Asignamos un valor al estado, va a ser un entero donde cada cifra corresponde
	 * a la ficha que esta en la casilla n
	 */
	public void calculaEstado(){
		this.estado = (tablero[0][0]*100000000) + (tablero[0][1]*10000000) + (tablero[0][2]*1000000) + (tablero[1][0]*100000) + 
					(tablero[1][1]*10000) + (tablero[1][2]*1000) + (tablero[2][0]*100) + (tablero[2][1]*10) + tablero[2][2];
	}
    /**
     * Si el estado actual es final devolvemos true sino devolvemos false
     * 
     * @return el nodo actual es final o no
     */
	public boolean esFinal(){    	
		return vHeur==0;
    }
   
	/**
	 * Generamos todos los hijos de un nodo
	 * 
	 * @return un vector con todos los hijos del nodo
	 */
	public Vector generaHijos(){
    	Vector nuevo=new Vector(0,1);
    	Nodo8Puzzle op1=this.Arriba();
    	Nodo8Puzzle op2=this.Derecha();
    	Nodo8Puzzle op3=this.Abajo();
    	Nodo8Puzzle op4=this.Izquierda();
    	if (op1!=null) nuevo.addElement(op1);
        if (op2!=null) nuevo.addElement(op2);
      	if (op3!=null) nuevo.addElement(op3);
      	if (op4!=null) nuevo.addElement(op4);          				
        return nuevo;
	} 
	

}
